package pri.zxx.learndemo.token;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ObjectUtils;
import pri.zxx.webdemo.utils.GsonUtil;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * @author Twillight
 */
public class JwtTokenFactory<T> {
    private static final Logger log = LoggerFactory.getLogger(JwtTokenFactory.class);

    //私钥
    public static String signingKey = "test";

    //签名算法
    private static final SignatureAlgorithm algorithm = SignatureAlgorithm.HS512;

    private static Key getKeyInstance(String signKey) {
        // We will sign our JavaWebToken with our ApiKey secret
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(signKey);
        return new SecretKeySpec(apiKeySecretBytes, algorithm.getJcaName());
    }

    /**
     * 生成token
     */
    public String createAccessJwtToken(T userInfo, Long number, TimeUnit unit) {
        Map<String, Object> claims = new HashMap<>();

        claims.put("data", GsonUtil.toJson(userInfo));

        LocalDateTime currentTime = LocalDateTime.now();

        return Jwts.builder()
                .setSubject("wahaha")
                .setClaims(claims)
                .setIssuedAt(Date.from(currentTime.atZone(ZoneId.systemDefault()).toInstant()))
//                .setExpiration(Date.from(currentTime.plusSeconds(unit.toSeconds(number)).atZone(ZoneId.systemDefault()).toInstant()))
                .signWith(algorithm, signingKey)
                .compact();
    }

    /**
     * 解析Token，同时也能验证Token，当验证失败返回null
     */
    public Optional<Jws<Claims>> parseJavaWebToken(String jwt) {
        try {
            return Optional.of(Jwts.parser().setSigningKey(getKeyInstance(signingKey))
                    .parseClaimsJws(jwt));
        } catch (Exception e) {
            log.error("json web token verify failed" + e.getMessage() + "::" + e.getCause());
            return Optional.empty();
        }
    }

    /**
     * 检查token有效性过期
     */
    public Boolean validToken(String jwt) {
        Optional<Jws<Claims>> optionalClaimsJws = this.parseJavaWebToken(jwt);
        if (optionalClaimsJws.isEmpty()) {
            return false;
        }
        Jws<Claims> claimsJws = optionalClaimsJws.get();
        if (claimsJws.getBody() == null || claimsJws.getBody().getExpiration() == null) {
            return true;
        }
        int i = claimsJws.getBody().getExpiration().toInstant().compareTo(Instant.now());
	    return i >= 0;
    }

    /**
     * 获取封装数据
     */
    public <T> Optional<T> getUserData(Jws<Claims> claimsJws, Class<? extends T> aClass) {
        if (claimsJws == null || claimsJws.getBody() == null || ObjectUtils.isEmpty(claimsJws.getBody().get("data"))) {
            return Optional.empty();
        } else {
            try {
                return Optional.of(GsonUtil.fromJson(claimsJws.getBody().get("data").toString(), aClass));
            } catch (Exception e) {
                log.error("---->>解析用户数据异常", e);
                return Optional.empty();
            }
        }
    }

    @Test
    public void myTest() {
        JwtTokenFactory<UserInfo> jwtTokenFactory = new JwtTokenFactory<>();
        UserInfo userInfo = new UserInfo();

        IntStream.range(1,100).forEach((i)->{
            userInfo.setUid("i")
                    .setUserType(1)
                    .setMobilephone("15282648585");
            long l = System.currentTimeMillis();
            String accessJwtToken = jwtTokenFactory.createAccessJwtToken(userInfo, 10L, TimeUnit.HOURS);
            log.warn("---->>token:{}", accessJwtToken);
            log.info("生产token时间：{}", System.currentTimeMillis()-l);
        });

//        UserInfo userInfo = new UserInfo();
//        userInfo.setUid("1L")
//                .setUserType(1)
//                .setMobilephone("15282648585");



//        stopWatch.start();
//        Optional<Jws<Claims>> optionalClaimsJws = jwtTokenFactory.parseJavaWebToken(accessJwtToken);
//
//        stopWatch.stop();
//        double totalTimeSeconds = stopWatch.getTotalTimeSeconds();
//        log.info("解析token时间：{}", totalTimeSeconds);
//
//        stopWatch.start();
//        Boolean aBoolean = jwtTokenFactory.validToken(accessJwtToken);
//        stopWatch.stop();
//        log.warn("---->>valid:{}", aBoolean);
//        log.info("解析token时间：{}", stopWatch.getTotalTimeMillis());
//
//        Optional<UserInfo> userData = jwtTokenFactory.getUserData(optionalClaimsJws.orElse(null), UserInfo.class);
//        log.warn("----->>data:{}", GsonUtil.toJson(userData.orElse(new UserInfo())));
    }
}
